package com.chatplus.application.service.file.impl;

import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.chatplus.application.common.exception.BadRequestException;
import com.chatplus.application.common.logging.SouthernQuietLogger;
import com.chatplus.application.common.logging.SouthernQuietLoggerFactory;
import com.chatplus.application.common.util.FileUtils;
import com.chatplus.application.dao.file.SysOssDao;
import com.chatplus.application.domain.entity.file.SysOssEntity;
import com.chatplus.application.file.core.OssClient;
import com.chatplus.application.file.entity.UploadResult;
import com.chatplus.application.file.enumd.AccessPolicyType;
import com.chatplus.application.file.factory.OssFactory;
import com.chatplus.application.service.file.SysOssService;
import jakarta.servlet.http.HttpServletResponse;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.tika.Tika;
import org.springframework.http.MediaType;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Collection;
import java.util.List;

/**
 * OSS对象存储表业务逻辑实现
 * <p>Table: t_sys_oss - OSS对象存储表</p>
 *
 * @author developer
 * @see SysOssEntity
 */
@Service
public class SysOssServiceImpl extends ServiceImpl<SysOssDao, SysOssEntity> implements SysOssService {
    private static final SouthernQuietLogger LOGGER = SouthernQuietLoggerFactory.getLogger(SysOssServiceImpl.class);
    protected final Tika tika;

    public SysOssServiceImpl() {
        this.tika = new Tika();
    }

    @Override
    public List<SysOssEntity> getByUrls(List<String> urlList) {
        LambdaQueryWrapper<SysOssEntity> lqw = Wrappers.lambdaQuery();
        lqw.in(SysOssEntity::getUrl, urlList);
        return baseMapper.selectList(lqw);
    }

    @Override
    public SysOssEntity getByOssIdOfMatchingUrl(Long ossId) {
        SysOssEntity sysOssEntity = baseMapper.selectById(ossId);
        if (sysOssEntity == null) {
            return null;
        }
        return setPrivateBucketAuthUrl(sysOssEntity);
    }

    @Override
    public void download(Long ossId, String fileName, HttpServletResponse response) throws IOException {
        SysOssEntity sysOss = getById(ossId);
        if (ObjectUtil.isNull(sysOss)) {
            throw new BadRequestException("文件数据不存在!");
        }
        if (fileName == null) {
            fileName = sysOss.getOriginalName();
        }
        FileUtils.setAttachmentResponseHeader(response, fileName);
        response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE + "; charset=UTF-8");
        OssClient storage = OssFactory.instance(sysOss.getService());
        try (InputStream inputStream = storage.getObjectContent(sysOss.getUrl())) {
            int available = inputStream.available();
            IoUtil.copy(inputStream, response.getOutputStream(), available);
        } catch (Exception e) {
            LOGGER.message("文件下载失败").exception(e).error();
            throw new BadRequestException("文件下载失败");
        }
    }

    @Override
    public void download(Long ossId, HttpServletResponse response) throws IOException {
        download(ossId, null, response);
    }

    @Override
    public SysOssEntity upload(MultipartFile file) {
        OssClient storage = OssFactory.instance();
        return upload(null, file, storage);
    }

    @Override
    public SysOssEntity upload(File file, String configKey) {
        OssClient storage = OssFactory.instance(configKey);
        SysOssEntity sysOssEntity = null;
        try {
            MultipartFile cMultiFile = new MockMultipartFile("file", file.getName(), null, new FileInputStream(file));
            sysOssEntity = upload(null, cMultiFile, storage);
        } catch (Exception e) {
            LOGGER.message("file转MultipartFile失败").exception(e).error();
        }
        return sysOssEntity;
    }

    @Override
    public SysOssEntity upload(File file) {
        OssClient storage = OssFactory.instance();
        SysOssEntity sysOssEntity = null;
        try {
            MultipartFile cMultiFile = new MockMultipartFile("file", file.getName(), null, new FileInputStream(file));
            sysOssEntity = upload(null, cMultiFile, storage);
        } catch (Exception e) {
            LOGGER.message("file转MultipartFile失败").exception(e).error();
        }
        return sysOssEntity;
    }

    @Override
    public SysOssEntity upload(MultipartFile file, String configKey) {
        OssClient storage = OssFactory.instance(configKey);
        return upload(null, file, storage);
    }

    @Override
    public SysOssEntity upload(MultipartFile file, String configKey, String hashId) {
        OssClient storage = OssFactory.instance(configKey);
        return upload(null, file, storage);
    }

    @Override
    public SysOssEntity upload(String customFileName, MultipartFile file, String configKey) {
        OssClient storage = OssFactory.instance(configKey);
        return upload(customFileName, file, storage);
    }

    private SysOssEntity upload(String fileName, MultipartFile file, OssClient storage) {
        String originalFileName = file.getOriginalFilename();
        if (StringUtils.isNotEmpty(fileName)) {
            originalFileName = fileName;
        }
        String suffix = "";
        if (StringUtils.isNotBlank(originalFileName)) {
            suffix = StringUtils.substring(originalFileName, originalFileName.lastIndexOf("."), originalFileName.length());
        }
        String contentType;
        String hashId;
        try (InputStream inputStream = file.getInputStream()) {
            contentType = tika.detect(inputStream);
            inputStream.reset();
            hashId = DigestUtils.sha256Hex(inputStream);
        } catch (Exception e) {
            LOGGER.message("文件上传失败-hash获取失败").exception(e).error();
            throw new BadRequestException("文件上传失败-hash获取失败");
        }
        SysOssEntity sysOssEntity = getByHashId(hashId);
        // 如果已经存在相同的文件而且没有被删除，则直接返回
        if (sysOssEntity != null && storage.checkFileExist(sysOssEntity.getUrl())) {
            return setPrivateBucketAuthUrl(sysOssEntity);
        }
        UploadResult uploadResult;
        try {
            uploadResult = storage.uploadSuffix(file.getBytes(), suffix, contentType);
        } catch (IOException e) {
            LOGGER.message("文件上传失败").exception(e).error();
            throw new BadRequestException("文件上传失败");
        }
        // 保存文件信息
        return buildResultEntity(originalFileName, hashId, contentType, suffix, storage.getConfigKey(), uploadResult);
    }

    private SysOssEntity buildResultEntity(String originalFileName
            , String hashId
            , String contentType
            , String suffix
            , String configKey
            , UploadResult uploadResult) {
        SysOssEntity oss = new SysOssEntity();
        oss.setUrl(uploadResult.getUrl());
        oss.setFileSuffix(suffix);
        oss.setFileName(uploadResult.getFilename());
        oss.setOriginalName(originalFileName);
        oss.setService(configKey);
        oss.setContentType(contentType);
        // 目前只作为标记使用，因为相同的文件hashId
        oss.setHashId(hashId);
        this.save(oss);
        return this.setPrivateBucketAuthUrl(oss);
    }

    @Override
    public Boolean deleteByIds(Collection<Long> ids) {
        List<SysOssEntity> list = baseMapper.selectBatchIds(ids);
        for (SysOssEntity sysOss : list) {
            OssClient storage = OssFactory.instance(sysOss.getService());
            storage.delete(sysOss.getUrl());
        }
        return baseMapper.deleteBatchIds(ids) > 0;
    }

    @Override
    public InputStream getFileInputStream(Long ossId) {
        SysOssEntity sysOssEntity = getById(ossId);
        if (sysOssEntity == null) {
            throw new BadRequestException("文件不存在或已丢失");
        }
        OssClient storage = OssFactory.instance(sysOssEntity.getService());
        InputStream inputStream = storage.getObjectContent(sysOssEntity.getUrl());
        if (inputStream == null) {
            throw new BadRequestException("文件不存在或已丢失");
        }
        return inputStream;
    }

    @Override
    public SysOssEntity getByHashId(String hashId) {
        if (StringUtils.isEmpty(hashId)) {
            return null;
        }
        LambdaQueryWrapper<SysOssEntity> lqw = Wrappers.lambdaQuery();
        lqw.eq(SysOssEntity::getHashId, hashId);
        lqw.orderByDesc(SysOssEntity::getUpdatedAt);
        lqw.last("LIMIT 1");
        return baseMapper.selectOne(lqw, false);
    }

    /**
     * 设置私有的Bucket的临时认证URL
     * 如果是公共的Bucket，不需要设置，则直接返回
     *
     * @param oss OSS对象
     * @return oss 匹配Url的OSS对象
     */
    private SysOssEntity setPrivateBucketAuthUrl(SysOssEntity oss) {
        OssClient storage = OssFactory.instance(oss.getService());
        // 仅修改桶类型为 private 的URL，临时URL时长为300s
        if (AccessPolicyType.PRIVATE == storage.getAccessPolicy()) {
            oss.setUrl(storage.getPrivateUrl(oss.getFileName(), 300));
        }
        return oss;
    }
}
